﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Management;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using MikuMikuLibrary.Archives;

namespace ft_module_parser
{
    public class ShaderAFT
    {
        struct nReplace
        {
            public string before;
            public string after;
            public nReplace(string before, string after)
            {
                this.before = before;
                this.after = after;
            }
        }

        public static void Process(string[] args, bool amd = false)
        {
            Console.WriteLine("ft_parser");
            Console.WriteLine(textconst.credits);
            Console.WriteLine("");

            //Console.WriteLine("shader last updated 14 sept 19");

            if (!File.Exists("shader.farc.bak"))
                if ((File.Exists("shader.farc")))
                    File.Copy("shader.farc", "shader.farc.bak");
                else
                {
                    Console.WriteLine("Copy Paste shader.farc into this folder!");
                    Console.ReadKey();
                    return;
                }

            Shader.Data shader = new Shader.Data();
            shader.farc = new MikuMikuLibrary.Archives.Farc.FarcArchive();
            shader.farc.Load("shader.farc.bak");

            foreach (var i in shader.farc.Entries)
            {
                var thefile = shader.farc.Open(i, MikuMikuLibrary.Archives.EntryStreamMode.MemoryStream);
                var streamreader = new StreamReader(thefile);
                string strmessage;
                strmessage = streamreader.ReadToEnd();
                var splitted = strmessage.Split(new string[] { "\n" }, StringSplitOptions.None);
                splitted[0] = splitted[0] + "\n" + "#" + i;
                if (amd)
                {
                    var newsplitted = new List<string>();
                    var nrep = new List<nReplace>();
                    nrep.Add(new nReplace("POW", "##POW"));
                    foreach (var ci in splitted)
                    {
                        bool patch = true;
                        var newstr = ci;
                        if (newstr.StartsWith("ATTRIB"))
                        {
                            var newstr2 = newstr.Replace("ATTRIB ", "").Replace(" = ", "=").Replace(";", "");
                            var nsplit = newstr2.Split(new string[] { "=" }, StringSplitOptions.None);
                            nrep.Add(new nReplace(nsplit[0], nsplit[1]));
                            patch = false;
                        }
                        if (newstr.StartsWith("PARAM"))
                        {
                            
                            if (!newstr.Contains("{"))
                            {
                                var newstr2 = newstr.Replace("PARAM ", "").Replace(" = ", "=").Replace(";", "");
                                var nsplit = newstr2.Split(new string[] { "=" }, StringSplitOptions.None);
                                nrep.Add(new nReplace(nsplit[0], nsplit[1]));
                            }
                            
                            patch = false;
                        }
                        if (newstr.Contains("OUTPUT"))
                        {
                            var newstr2 = newstr.Replace("OUTPUT ", "").Replace(" = ", "=").Replace("SHORT ", "").Replace("LONG ", "").Replace(";", "");
                            var nsplit = newstr2.Split(new string[] { "=" }, StringSplitOptions.None);
                            nrep.Add(new nReplace(nsplit[0], nsplit[1]));
                            patch = false;
                        }
                        if (newstr.StartsWith("ADDRESS"))
                            patch = false;
                        foreach (var chg in nrep)
                        {
                            //Console.WriteLine(chg.before + "," + chg.after); 
                            if (patch)
                                newstr = newstr.Replace(chg.before, chg.after);
                        }
                        newsplitted.Add(newstr);
                    }
                    strmessage = String.Join("\n", newsplitted);
                } else strmessage = String.Join("\n", splitted);
                //strmessage = strmessage + "\n" + "# " + i;
                byte[] byteArray = Encoding.Default.GetBytes(strmessage);
                MemoryStream stream = new MemoryStream(byteArray);
                shader.farc.Add(i, stream, true, ConflictPolicy.Replace);
            }

            List<string> gpushortname = new List<string>();
            try
            {
                var gpus = NvAPIWrapper.GPU.PhysicalGPU.GetPhysicalGPUs();
                foreach (var i in gpus)
                {
                    Console.WriteLine(i.ArchitectInformation.ShortName);
                    gpushortname.Add(i.ArchitectInformation.ShortName.ToUpper());
                }
            }
            catch (Exception e)
            {
                if (amd)
                {
                    gpushortname.Add("TU");
                }
                else
                {
                    Console.WriteLine("NvApi Error " + e.Message);
                    Console.Write("Type your GPU Code Name (e.g GM107) : ");
                    gpushortname.Add(Console.ReadLine());
                }
            }
            var cdef = new WebClient();
            try
            {
                cdef.DownloadFile("https://notabug.org/lybxlpsv/apdshd/raw/master/shd.txt?t=" + (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds, Path.GetTempPath() + "lybshd.txt");
            }
            catch (Exception Ex)
            {
                Console.WriteLine("Download failed!");
            }
            if (!File.Exists(Path.GetTempPath() + "lybshd.txt"))
            {
                Console.WriteLine("Requires internet connection on first run!");
                Console.ReadKey();
                return;
            }

            var lines = File.ReadAllLines(Path.GetTempPath() + "lybshd.txt");
            foreach (var i in lines)
            {
                if (i.StartsWith("#"))
                    Console.WriteLine(i.Remove(0,1));
                else
                {
                    string[] split1 = i.Split(new string[] { "|!|" }, StringSplitOptions.None);
                    string[] split2 = split1[0].Split('|');
                    bool patch = false;
                    string fname = "";
                    int linec = -1;
                    foreach (var c in split2)
                    {
                        if (c.StartsWith("!"))
                        {
                            fname = c.Split('@')[0].Remove(0, 1) + ".txt";
                            if (c.Split('@').Length != 1)
                                linec = int.Parse(c.Split('@')[1]);
                            if (File.Exists(fname))
                                patch = true;
                        }

                        foreach (var g in gpushortname)
                        {
                            var culture = new CultureInfo("en-us");
                            if (culture.CompareInfo.IndexOf(g, c, CompareOptions.IgnoreCase) >= 0)
                                patch = true;
                        }
                        // if (gpushortname.Any(d => d.Contains(c)))
                        //    patch = true;
                    }
                    if (patch)
                    {
                        string before = split1[1];
                        string after = split1[2];
                        string regex = split1[3];
                        if (linec != -1)
                            if (fname != "")
                            {
                                try
                                {
                                    var cstr = File.ReadAllLines(fname);
                                    after = after.Replace("<line>", cstr[linec]);
                                }
                                catch
                                {
                                    after = after.Replace("<line>", split1[4]);
                                }
                            }
                        //Console.WriteLine(before + "|" + after + "|" + regex);
                        shader.str.Add(new Shader.StrReplace(before, after, regex));
                    }
                }
            }

            /*
            if (turing)
            {
                Console.WriteLine("Turing Detected!");
                shader.str.Add(new Shader.StrReplace("", "", "*."));
                shader.str.Add(new Shader.StrReplace("RCP sum.y, sum.y;\r\n", "RCP sum.y, sum.y;\r\nADD sum.x, sum.x, 0.000300;\r\n", "esm_gauss.0.fp"));
                shader.str.Add(new Shader.StrReplace("MUL _tmp0.xz, ybr, 2.0; POW _tmp0.x, |_tmp0.x|, 0.85; SSG _tmp0.yw, ybr.xxzz; POW _tmp0.z, |_tmp0.z|, 0.85; MUL _tmp0.yw, _tmp0, 0.5; MUL tmp.xz, _tmp0.xxzz, _tmp0.yyww;",
                    "MUL _tmp0.xz, ybr, 2.0; POW _tmp0.x, |_tmp0.x|, 0.85; TEMP _tmpForSSG; SGE _tmpForSSG.xz, ybr.xxzz, 0; SUB _tmpForSSG.xz, _tmpForSSG.xxzz, 1; SGT _tmpForSSG.yw, ybr.xxzz, 0; ADD _tmp0.yw, _tmpForSSG.xxzz, _tmpForSSG.yyww; POW _tmp0.z, |_tmp0.z|, 0.85; MUL _tmp0.yw, _tmp0, 0.5; MUL tmp.xz, _tmp0.xxzz, _tmp0.yyww;", ".*npr.*"));
            }
            if (File.Exists("toon.txt"))
            {
                var lines = File.ReadAllLines("toon.txt");
                shader.str.Add(new Shader.StrReplace("MAD tmp.y, lc.z, 1.2, -0.5;", "MAD tmp.y, lc.z, " + lines[0] + ";", ".*cloth_npr.*"));
                shader.str.Add(new Shader.StrReplace("MAD diff.xyz, spec, spec_ratio, diff;", "#MAD diff.xyz, spec, spec_ratio, diff;", "skin_default.*"));
            }
            */
            shader.ApplyStrReplace();
            shader.farc.Save("shader.farc");
        }
    }
}
